// Copyright (c) 2009 http://grommet.codeplex.com
// This source is subject to the Microsoft Public License.
// See http://www.opensource.org/licenses/ms-pl.html
// All other rights reserved.

using System;
using System.ComponentModel;
using System.Threading;
using Grommet.Ext;

#if MicroFramework
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
#endif

namespace Grommet.Net.XBee
{
    public class CurrentNodeInfo : Info, INotifyPropertyChanged
    {
        private string identifier = "Default";
        private ushort address = 0;
        private ushort parentAddress = 0;
        private ulong serialNumber = 0;
        private ushort maximumRFPayloadSize = 64;
        private ushort firmwareVersion = 0;
        private ushort hardwareVersion = 0;

        public string Identifier 
        {
            get { return identifier; }
            internal set
            {
                identifier = value;
                OnPropertyChanged("Identifier");
            }
        }

        public ushort Address 
        {
            get { return address; }
            internal set
            {
                address = value;
                OnPropertyChanged("Address");
            }
        }

        public ushort ParentAddress 
        {
            get { return parentAddress; }
            internal set
            {
                parentAddress = value;
                OnPropertyChanged("ParentAddress");
            }
        }

        public ulong SerialNumber 
        {
            get { return serialNumber; }
            internal set
            {
                serialNumber = value;
                OnPropertyChanged("SerialNumber");
            }
        }

        public ushort MaximumRFPayloadSize 
        {
            get { return maximumRFPayloadSize; }
            internal set
            {
                maximumRFPayloadSize = value;
                OnPropertyChanged("MaximumRFPayloadSize");
            }
        }

        public ushort FirmwareVersion 
        {
            get { return firmwareVersion; }
            internal set
            {
                firmwareVersion = value;
                OnPropertyChanged("FirmwareVersion");
            }
        }

        public ushort HardwareVersion 
        {
            get { return hardwareVersion; }
            internal set
            {
                hardwareVersion = value;
                OnPropertyChanged("HardwareVersion");
            }
        }

        internal void ProcessCommandResult(byte[] data)
        {
            XBeeCommand command = (XBeeCommand)BigEndianConvert.ToUInt16(data, 2);
            if (data[4] != 0)
            {
                // An error occured, ignore the result
                // It's ok here, some command will error on different device types.
                return;
            }

            switch (command)
            {
                case XBeeCommand.Address:
                    Address = (ushort)GetValue(data, 5, 2);
                    break;
                case XBeeCommand.ParentAddress:
                    ParentAddress = (ushort)GetValue(data, 5, 2);
                    break;
                case XBeeCommand.SerialNumberHigh:
                    SerialNumber = (SerialNumber & 0x00000000FFFFFFFF) | (((ulong)GetValue(data, 5, 4)) << 32);
                    break;
                case XBeeCommand.SerialNumberLow:
                    SerialNumber = (SerialNumber & 0xFFFFFFFF00000000) | GetValue(data, 5, 4);
                    break;
                case XBeeCommand.NodeIdentifier:
                    Identifier = ExtractString(data, 5);
                    break;
                case XBeeCommand.MaximumRFPayloadBytes:
                    // good packet
                    MaximumRFPayloadSize = (ushort)GetValue(data, 5, 2);
                    break;
                case XBeeCommand.FirmwareVersion:
                    FirmwareVersion = (ushort)GetValue(data, 5, 2);
                    break;
                case XBeeCommand.HardwareVersion:
                    HardwareVersion = (ushort)GetValue(data, 5, 2);
                    break;
                default:
                    break;
            }
        }

        private uint GetValue(byte[] data, int startIndex, int size)
        {
            uint value = 0;
            for (int i = 0; i < size; i++)
            {
                value = (value << 8) | data[startIndex + i];
            }
            return value;
        }

        internal void Refresh(XBeeModule module)
        {
            module.SendCommand(XBeeCommand.MaximumRFPayloadBytes, (f) => ProcessCommandResult(f.FrameData));
            module.SendCommand(XBeeCommand.NodeIdentifier, (f) => ProcessCommandResult(f.FrameData));
            module.SendCommand(XBeeCommand.Address, (f) => ProcessCommandResult(f.FrameData));
            module.SendCommand(XBeeCommand.ParentAddress, (f) => ProcessCommandResult(f.FrameData));
            module.SendCommand(XBeeCommand.SerialNumberHigh, (f) => ProcessCommandResult(f.FrameData));
            module.SendCommand(XBeeCommand.SerialNumberLow, (f) => ProcessCommandResult(f.FrameData));
            module.SendCommand(XBeeCommand.FirmwareVersion, (f) => ProcessCommandResult(f.FrameData));
            module.SendCommand(XBeeCommand.HardwareVersion, (f) => ProcessCommandResult(f.FrameData));
        }

        #region INotifyPropertyChanged Members

        private void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;

            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }
}
